home *** CD-ROM | disk | FTP | other *** search
/ Exame Informatica 139 / Exame Informatica 139.iso / Internet / NVU / chrome / toolkit.jar / content / mozapps / extensions / extensions.js < prev    next >
Encoding:
Text File  |  2005-01-31  |  34.5 KB  |  929 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Globals
  3.  
  4. const kObserverServiceProgID = "@mozilla.org/observer-service;1";
  5. const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
  6.  
  7. var gExtensionManager = null;
  8. var gExtensionsView   = null;
  9. var gWindowState      = "";
  10. var gURIPrefix        = ""; // extension or theme prefix
  11. var gDSRoot           = ""; // extension or theme root
  12. var gGetMoreURL       = "";
  13. var gCurrentTheme     = "";
  14. var gDownloadManager  = null;
  15. var gObserverIndex    = -1;
  16. var gItemType         = -1;
  17.  
  18. const PREF_APP_ID                           = "app.id";
  19. const PREF_EXTENSIONS_GETMORETHEMESURL      = "extensions.getMoreThemesURL";
  20. const PREF_EXTENSIONS_GETMOREEXTENSIONSURL  = "extensions.getMoreExtensionsURL";
  21. const PREF_EXTENSIONS_DSS_ENABLED           = "extensions.dss.enabled";
  22. const PREF_EXTENSIONS_DSS_SWITCHPENDING     = "extensions.dss.switchPending";
  23. const PREF_EM_LAST_SELECTED_SKIN            = "extensions.lastSelectedSkin";
  24. const PREF_GENERAL_SKINS_SELECTEDSKIN       = "general.skins.selectedSkin";
  25.  
  26. const KEY_DEFAULT_THEME = "classic/1.0";
  27.  
  28. ///////////////////////////////////////////////////////////////////////////////
  29. // Utility Functions 
  30.  
  31. const PREFIX_EXTENSION  = "urn:mozilla:extension:";
  32. const PREFIX_THEME      = "urn:mozilla:theme:";
  33.  
  34.  
  35. function getItemPrefix(aItemType)
  36. {
  37.   var prefix = "";
  38.   if (aItemType & nsIUpdateItem.TYPE_EXTENSION) 
  39.     prefix = PREFIX_EXTENSION;
  40.   else if (aItemType & nsIUpdateItem.TYPE_THEME)
  41.     prefix = PREFIX_THEME;
  42.   return prefix;
  43. }
  44.  
  45. function stripPrefix(aURI, aItemType)
  46. {
  47.   var val = aURI;
  48.   if (aItemType == nsIUpdateItem.TYPE_ADDON)
  49.     val = stripPrefix(aURI, getItemType(aURI));
  50.   else {
  51.     var prefix = getItemPrefix(aItemType);
  52.     if (prefix && aURI.substr(0, prefix.length) == prefix)  
  53.       val = aURI.substr(prefix.length, aURI.length);
  54.   }
  55.   return val;
  56. }
  57.  
  58. function openURL(aURL)
  59. {
  60.   var uri = Components.classes["@mozilla.org/network/standard-url;1"]
  61.                       .createInstance(Components.interfaces.nsIURI);
  62.   uri.spec = aURL;
  63.  
  64.   var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
  65.                               .getService(Components.interfaces.nsIExternalProtocolService);
  66.   protocolSvc.loadUrl(uri);
  67. }
  68.  
  69. function flushDataSource()
  70. {
  71.   var rds = gExtensionManager.datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  72.   if (rds)
  73.     rds.Flush();
  74. }
  75.  
  76. function setRestartMessage(aItem)
  77. {
  78.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  79.                       .getService(Components.interfaces.nsIStringBundleService);
  80.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  81.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  82.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  83.   var themeName = aItem.getAttribute("name");
  84.   var restartMessage = extensionStrings.formatStringFromName("dssSwitchAfterRestart", 
  85.                                                              [brandShortName], 1);
  86.   aItem.setAttribute("creator", restartMessage);
  87. }
  88.  
  89. ///////////////////////////////////////////////////////////////////////////////
  90. // Event Handlers
  91. function onExtensionSelect(aEvent)
  92. {
  93.   if (aEvent.target.selected)
  94.     aEvent.target.setAttribute("last-selected", aEvent.target.selected.id);
  95.   else
  96.     aEvent.target.removeAttribute("last-selected");
  97. }
  98.  
  99. ///////////////////////////////////////////////////////////////////////////////
  100. // Startup, Shutdown
  101. function Startup() 
  102. {
  103.   gWindowState = window.location.search.substr("?type=".length, window.location.search.length);
  104.   
  105.   var isExtensions = gWindowState == "extensions";
  106.   gURIPrefix  = isExtensions ? "urn:mozilla:extension:" : "urn:mozilla:theme:";
  107.   gDSRoot     = isExtensions ? "urn:mozilla:extension:root" : "urn:mozilla:theme:root";
  108.   gItemType   = isExtensions ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME;
  109.   
  110.   document.documentElement.setAttribute("windowtype", document.documentElement.getAttribute("windowtype") + "-" + gWindowState);
  111.  
  112.   gExtensionsView = document.getElementById("extensionsView");
  113.   gExtensionsView.setAttribute("state", gWindowState);
  114.   gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
  115.                                 .getService(Components.interfaces.nsIExtensionManager);
  116.   
  117.   // Extension Command Updating is handled by a command controller.
  118.   gExtensionsView.controllers.appendController(gExtensionsViewController);
  119.  
  120.   // This persists the last-selected extension
  121.   gExtensionsView.addEventListener("richview-select", onExtensionSelect, false);
  122.  
  123.   // Finally, update the UI. 
  124.   gExtensionsView.database.AddDataSource(gExtensionManager.datasource);
  125.   gExtensionsView.setAttribute("ref", gDSRoot);
  126.   gExtensionsView.focus();
  127.   
  128.   var pref = Components.classes["@mozilla.org/preferences-service;1"]
  129.                        .getService(Components.interfaces.nsIPrefBranch);
  130.   if (!isExtensions) {
  131.     gExtensionsView.addEventListener("richview-select", onThemeSelect, false);
  132.  
  133.     if (pref.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN))
  134.       gCurrentTheme = pref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
  135.     else
  136.       gCurrentTheme = KEY_DEFAULT_THEME;
  137.       
  138.     var useThemeButton = document.getElementById("useThemeButton");
  139.     useThemeButton.hidden = false;
  140.  
  141.     var optionsButton = document.getElementById("optionsButton");
  142.     optionsButton.hidden = true;
  143.     
  144.     var pref = Components.classes["@mozilla.org/preferences-service;1"]
  145.                          .getService(Components.interfaces.nsIPrefBranch);
  146.     if (!pref.getBoolPref(PREF_EXTENSIONS_DSS_ENABLED) && 
  147.         pref.getBoolPref(PREF_EXTENSIONS_DSS_SWITCHPENDING)) {
  148.       var lastSelectedSkin = pref.getCharPref(PREF_EM_LAST_SELECTED_SKIN);
  149.       for (var i = 0; i < gExtensionsView.childNodes.length; ++i) {
  150.         var item = gExtensionsView.childNodes[i];
  151.         if (item.getAttribute("internalName") == lastSelectedSkin) {
  152.           setRestartMessage(item);
  153.           break;
  154.         }
  155.       }
  156.     }
  157.   }
  158.   
  159.   // Restore the last-selected extension
  160.   var lastSelected = gExtensionsView.getAttribute("last-selected");
  161.   if (lastSelected != "")
  162.     lastSelected = document.getElementById(lastSelected);
  163.   if (!lastSelected) 
  164.     gExtensionsView.selectionForward();
  165.   else
  166.     gExtensionsView.selected = lastSelected;
  167.  
  168.   var extensionsStrings = document.getElementById("extensionsStrings");
  169.   document.documentElement.setAttribute("title", extensionsStrings.getString(gWindowState + "Title"));
  170.   
  171.   gExtensionsViewController.onCommandUpdate(); 
  172.   
  173.   gGetMoreURL = pref.getComplexValue(isExtensions ? PREF_EXTENSIONS_GETMOREEXTENSIONSURL 
  174.                                                   : PREF_EXTENSIONS_GETMORETHEMESURL, 
  175.                                      Components.interfaces.nsIPrefLocalizedString).data;
  176.   gGetMoreURL = gGetMoreURL.replace(/%APPID%/g, pref.getCharPref(PREF_APP_ID));
  177.   // Update various pieces of state-dependant UI
  178.   var getMore = document.getElementById("getMore");
  179.   getMore.setAttribute("value", getMore.getAttribute(isExtensions ? "valueextensions" : "valuethemes"));
  180.   getMore.setAttribute("tooltiptext", getMore.getAttribute(isExtensions ? "tooltiptextextensions" : "tooltiptextthemes"));
  181.   
  182.   if (!isExtensions) {
  183.     var themePreviewArea = document.getElementById("themePreviewArea");
  184.     themePreviewArea.hidden = false;
  185.     gExtensionsView.removeAttribute("flex");
  186.   }
  187.   
  188.   // Set Initial Size
  189.   var win = document.documentElement;
  190.   if (!win.hasAttribute("width") || !win.hasAttribute("height")) {
  191.     win.setAttribute("width", isExtensions ? 460 : 560);
  192.     win.setAttribute("height", isExtensions ? 300 : 380);
  193.   }
  194.  
  195.   // Now look and see if we're being opened by XPInstall
  196.   gDownloadManager = new XPInstallDownloadManager();
  197.   var os = Components.classes["@mozilla.org/observer-service;1"]
  198.                      .getService(Components.interfaces.nsIObserverService);
  199.   os.addObserver(gDownloadManager, "xpinstall-download-started", false);
  200.  
  201.   gObserverIndex = gExtensionManager.addDownloadObserver(gDownloadManager);
  202.   
  203.   if ("arguments" in window) {
  204.     try {
  205.       var params = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock);
  206.       gDownloadManager.addDownloads(params);
  207.     }
  208.     catch (e) { }
  209.   }
  210.   
  211.   // Set the tooltips
  212.   if (!isExtensions) {
  213.     var extensionsStrings = document.getElementById("extensionsStrings");
  214.     document.getElementById("installButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdInstallTooltipTheme"));
  215.     document.getElementById("uninstallButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdUninstallTooltipTheme"));
  216.     document.getElementById("updateButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdUpdateTooltipTheme"));
  217.   }
  218. }
  219.  
  220. function Shutdown() 
  221. {
  222.   if (gWindowState != "extensions")
  223.     gExtensionsView.removeEventListener("richview-select", onThemeSelect, false);
  224.   
  225.   gExtensionManager.removeDownloadObserverAt(gObserverIndex);
  226.  
  227.   var os = Components.classes["@mozilla.org/observer-service;1"]
  228.                      .getService(Components.interfaces.nsIObserverService);
  229.   if (gDownloadManager) 
  230.     os.removeObserver(gDownloadManager, "xpinstall-download-started");
  231. }
  232.  
  233. ///////////////////////////////////////////////////////////////////////////////
  234. //
  235. // XPInstall
  236. //
  237.  
  238. function getURLSpecFromFile(aFile)
  239. {
  240.   var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  241.                           .getService(Components.interfaces.nsIIOService);
  242.   var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  243.   return fph.getURLSpecFromFile(aFile);
  244. }
  245.  
  246. function XPInstallDownloadManager()
  247. {
  248.   var extensionsStrings = document.getElementById("extensionsStrings");
  249.   this._statusFormatKBMB  = extensionsStrings.getString("statusFormatKBMB");
  250.   this._statusFormatKBKB  = extensionsStrings.getString("statusFormatKBKB");
  251.   this._statusFormatMBMB  = extensionsStrings.getString("statusFormatMBMB");
  252. }
  253.  
  254. XPInstallDownloadManager.prototype = {
  255.   _statusFormat     : null,
  256.   _statusFormatKBMB : null,
  257.   _statusFormatKBKB : null,
  258.   _statusFormatMBMB : null,
  259.   
  260.   observe: function (aSubject, aTopic, aData) 
  261.   {
  262.     switch (aTopic) {
  263.     case "xpinstall-download-started":
  264.       var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray);
  265.       var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer);
  266.       paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock);
  267.       this.addDownloads(paramBlock);
  268.       break;
  269.     }
  270.   },
  271.   
  272.   addDownloads: function (aParams)
  273.   {
  274.     var numXPInstallItems = aParams.GetInt(1);
  275.     var isExtensions = gWindowState == "extensions";
  276.     
  277.     var items = [];
  278.     for (var i = 0; i < numXPInstallItems;) {
  279.       var displayName = aParams.GetString(i++);
  280.       var url = aParams.GetString(i++);
  281.       var iconURL = aParams.GetString(i++);
  282.       if (!iconURL) { 
  283.         iconURL = isExtensions ? "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png" : 
  284.                                  "chrome://mozapps/skin/extensions/themeGeneric.png";
  285.       }
  286.       
  287.       var type = isExtensions ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME;
  288.       // gExtensionManager.addDownload(displayName, url, iconURL, type);
  289.       var item = Components.classes["@mozilla.org/updates/item;1"]
  290.                            .createInstance(Components.interfaces.nsIUpdateItem);
  291.       item.init(url, " ", "", "", displayName, -1, url, iconURL, "", type);
  292.       items.push(item);
  293.  
  294.       // Advance the enumerator
  295.       var certName = aParams.GetString(i++);
  296.     }
  297.     
  298.     gExtensionManager.addDownloads(items, items.length);
  299.   },
  300.  
  301.   removeDownload: function (aEvent)
  302.   {
  303.   
  304.   },
  305.   
  306.   /////////////////////////////////////////////////////////////////////////////  
  307.   // nsIExtensionDownloadProgressListener
  308.   onStateChange: function (aURL, aState, aValue)
  309.   {
  310.     const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
  311.     var element = document.getElementById(aURL);
  312.     if (!element) return;
  313.     switch (aState) {
  314.     case nsIXPIProgressDialog.DOWNLOAD_START:
  315.       element.setAttribute("state", "waiting");
  316.       element.setAttribute("progress", "0");
  317.       break;
  318.     case nsIXPIProgressDialog.DOWNLOAD_DONE:
  319.       element.setAttribute("progress", "100");
  320.       break;
  321.     case nsIXPIProgressDialog.INSTALL_START:
  322.       element.setAttribute("state", "installing");
  323.       break;
  324.     case nsIXPIProgressDialog.INSTALL_DONE:
  325.       dump("*** state change = " + aURL + ", state = " + aState + ", value = " + aValue + "\n");
  326.       element.setAttribute("state", "done");
  327.       var msg;
  328.       if (aValue != 0) {
  329.         var xpinstallStrings = document.getElementById("xpinstallStrings");
  330.         try {
  331.           msg = xpinstallStrings.getString("error" + aValue);
  332.         }
  333.         catch (e) {
  334.           msg = xpinstallStrings.getFormattedString("unknown.error", [aValue]);
  335.         }
  336.         var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  337.                             .getService(Components.interfaces.nsIStringBundleService);
  338.         var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  339.         var title = extensionStrings.GetStringFromName("errorInstallTitle");
  340.  
  341.         var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  342.         var brandShortName = brandStrings.GetStringFromName("brandShortName");
  343.         var params = [brandShortName, aURL, msg];
  344.         var message = extensionStrings.formatStringFromName("errorInstallMessage", params, params.length);
  345.         
  346.         var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  347.                            .getService(Components.interfaces.nsIPromptService);
  348.         ps.alert(window, title, message);
  349.         element.setAttribute("status", msg);
  350.  
  351.       }
  352.       // Remove the dummy, since we installed successfully
  353.       var type = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION 
  354.                                               : nsIUpdateItem.TYPE_THEME;
  355.       gExtensionManager.removeDownload(aURL, type);
  356.       break;
  357.     case nsIXPIProgressDialog.DIALOG_CLOSE:
  358.       break;
  359.     }
  360.   },
  361.   
  362.   _urls: { },
  363.   onProgress: function (aURL, aValue, aMaxValue)
  364.   {
  365.     var element = document.getElementById(aURL);
  366.     if (!element) return;
  367.     var percent = Math.round((aValue / aMaxValue) * 100);
  368.     if (percent > 1 && !(aURL in this._urls)) {
  369.       this._urls[aURL] = true;
  370.       element.setAttribute("state", "downloading");
  371.     }
  372.     element.setAttribute("progress", percent);
  373.     
  374.     var KBProgress = parseInt(aValue/1024 + .5);
  375.     var KBTotal = parseInt(aMaxValue/1024 + .5);
  376.     element.setAttribute("status", this._formatKBytes(KBProgress, KBTotal));
  377.   },
  378.   
  379.   _replaceInsert: function ( text, index, value ) 
  380.   {
  381.     var result = text;
  382.     var regExp = new RegExp( "#"+index );
  383.     result = result.replace( regExp, value );
  384.     return result;
  385.   },
  386.   
  387.   // aBytes     aTotalKBytes    returns:
  388.   // x, < 1MB   y < 1MB         x of y KB
  389.   // x, < 1MB   y >= 1MB        x KB of y MB
  390.   // x, >= 1MB  y >= 1MB        x of y MB
  391.   _formatKBytes: function (aKBytes, aTotalKBytes)
  392.   {
  393.     var progressHasMB = parseInt(aKBytes/1000) > 0;
  394.     var totalHasMB = parseInt(aTotalKBytes/1000) > 0;
  395.     
  396.     var format = "";
  397.     if (!progressHasMB && !totalHasMB) {
  398.       format = this._statusFormatKBKB;
  399.       format = this._replaceInsert(format, 1, aKBytes);
  400.       format = this._replaceInsert(format, 2, aTotalKBytes);
  401.     }
  402.     else if (progressHasMB && totalHasMB) {
  403.       format = this._statusFormatMBMB;
  404.       format = this._replaceInsert(format, 1, (aKBytes / 1000).toFixed(1));
  405.       format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1));
  406.     }
  407.     else if (totalHasMB && !progressHasMB) {
  408.       format = this._statusFormatKBMB;
  409.       format = this._replaceInsert(format, 1, aKBytes);
  410.       format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1));
  411.     }
  412.     else {
  413.       // This is an undefined state!
  414.       dump("*** huh?!\n");
  415.     }
  416.     
  417.     return format;  
  418.   },
  419.  
  420.   /////////////////////////////////////////////////////////////////////////////
  421.   // nsISupports
  422.   QueryInterface: function (aIID) 
  423.   {
  424.     if (!aIID.equals(Components.interfaces.nsIExtensionDownloadProgressListener) &&
  425.         !aIID.equals(Components.interfaces.nsISupports))
  426.       throw Components.results.NS_ERROR_NO_INTERFACE;
  427.     return this;
  428.   }
  429. };
  430.  
  431. ///////////////////////////////////////////////////////////////////////////////
  432. //
  433. // View Event Handlers
  434. //
  435. function onViewDoubleClick(aEvent)
  436. {
  437.   if (aEvent.button != 0)
  438.     return;
  439.  
  440.   switch (gWindowState) {
  441.   case "extensions":
  442.     gExtensionsViewController.doCommand('cmd_options');
  443.     break;
  444.   case "themes":
  445.     gExtensionsViewController.doCommand('cmd_useTheme');
  446.     break;
  447.   }
  448. }
  449.  
  450. function onThemeSelect(aEvent)
  451. {
  452.   if (gWindowState != "themes")
  453.     return;
  454.  
  455.   var previewImageDeck = document.getElementById("previewImageDeck");
  456.   if (!gExtensionsView.selected) {
  457.     previewImageDeck.setAttribute("selectedIndex", "0");
  458.     return;
  459.   }
  460.   var url = gExtensionsView.selected.getAttribute("previewImage");
  461.   if (url) {
  462.     previewImageDeck.setAttribute("selectedIndex", "2");
  463.     var previewImage = document.getElementById("previewImage");
  464.     previewImage.setAttribute("src", url);
  465.   }
  466.   else
  467.     previewImageDeck.setAttribute("selectedIndex", "1");
  468. }
  469.  
  470. ///////////////////////////////////////////////////////////////////////////////
  471. // View Context Menus
  472. var gExtensionContextMenus = ["menuitem_options", "menuitem_homepage", "menuitem_about", 
  473.                               "menuseparator_1", "menuitem_uninstall", "menuitem_update",
  474.                               "menuitem_enable", "menuitem_disable", "menuseparator_2", 
  475.                               "menuitem_moveTop", "menuitem_moveUp", "menuitem_moveDn"];
  476. var gThemeContextMenus = ["menuitem_useTheme", "menuitem_homepage", "menuitem_about", 
  477.                           "menuseparator_1", "menuitem_uninstall", "menuitem_update",
  478.                           "menuitem_enable"];
  479.  
  480. function buildContextMenu(aEvent)
  481. {
  482.   if (aEvent.target.id != "extensionContextMenu")
  483.     return false;
  484.     
  485.   var popup = document.getElementById("extensionContextMenu");
  486.   while (popup.hasChildNodes())
  487.     popup.removeChild(popup.firstChild);
  488.  
  489.   var isExtensions = gWindowState == "extensions";
  490.  
  491.   var menus = isExtensions ? gExtensionContextMenus : gThemeContextMenus;  
  492.   for (var i = 0; i < menus.length; ++i) {
  493.     var clonedMenu = document.getElementById(menus[i]).cloneNode(true);
  494.     clonedMenu.id = clonedMenu.id + "_clone";
  495.     popup.appendChild(clonedMenu);
  496.   }
  497.  
  498.   var extensionsStrings = document.getElementById("extensionsStrings");
  499.   var menuitem_about = document.getElementById("menuitem_about_clone");
  500.   var selectedItem = gExtensionsView.selected;
  501.   var name = selectedItem ? selectedItem.getAttribute("name") : "";
  502.   menuitem_about.setAttribute("label", extensionsStrings.getFormattedString("aboutExtension", [name]));
  503.   
  504.   if (isExtensions) {
  505.     var canEnable = gExtensionsViewController.isCommandEnabled("cmd_reallyEnable");
  506.     var menuitemToShow, menuitemToHide;
  507.     if (canEnable) {
  508.       menuitemToShow = document.getElementById("menuitem_enable_clone");
  509.       menuitemToHide = document.getElementById("menuitem_disable_clone");
  510.     }
  511.     else {
  512.       menuitemToShow = document.getElementById("menuitem_disable_clone");
  513.       menuitemToHide = document.getElementById("menuitem_enable_clone");
  514.     }
  515.     menuitemToShow.hidden = false;
  516.     menuitemToHide.hidden = true;
  517.   }
  518.   else {
  519.     var enableMenu = document.getElementById("menuitem_enable_clone");
  520.     if (gExtensionsView.selected.getAttribute("compatible") == "false" ||
  521.         gExtensionsView.selected.disabled) 
  522.       // don't let the user activate incompatible themes, but show a (disabled) Enable
  523.       // menuitem to give visual feedback; it's disabled because cmd_enable returns false
  524.       enableMenu.hidden = false;
  525.     else {
  526.       enableMenu.hidden = true;
  527.     }
  528.   }
  529.     
  530.   return true;
  531. }
  532.  
  533. ///////////////////////////////////////////////////////////////////////////////
  534. // Drag and Drop
  535.  
  536. var gExtensionsDNDObserver =
  537. {
  538.   _ioServ: null,
  539.   _filePH: null,
  540.   
  541.   _ensureServices: function ()
  542.   {
  543.     if (!this._ioServ) {
  544.       this._ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  545.                                .getService(Components.interfaces.nsIIOService);
  546.       this._filePH = this._ioServ.getProtocolHandler("file")
  547.                          .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  548.     }
  549.   },
  550.   
  551.   onDragOver: function (aEvent, aFlavor, aDragSession)
  552.   {
  553.     this._ensureServices();
  554.   
  555.     aDragSession.canDrop = true;
  556.     var count = aDragSession.numDropItems;
  557.     for (var i = 0; i < count; ++i) {
  558.       var xfer = Components.classes["@mozilla.org/widget/transferable;1"]
  559.                           .createInstance(Components.interfaces.nsITransferable);
  560.       xfer.addDataFlavor("text/x-moz-url");
  561.       aDragSession.getData(xfer, i);
  562.       
  563.       var data = { }, length = { };
  564.       xfer.getTransferData("text/x-moz-url", data, length);
  565.       var fileURL = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
  566.  
  567.       var fileURI = this._ioServ.newURI(fileURL, null, null);
  568.       var url = fileURI.QueryInterface(Components.interfaces.nsIURL);
  569.       if (url.fileExtension != "jar" && url.fileExtension != "xpi") {
  570.         aDragSession.canDrop = false;
  571.         break;
  572.       }
  573.     }
  574.   },
  575.   
  576.   onDrop: function(aEvent, aXferData, aDragSession)
  577.   {
  578.     this._ensureServices();
  579.     
  580.     var xpinstallObj = {};
  581.     var themes = {};
  582.     var xpiCount = 0;
  583.     var themeCount = 0;
  584.     
  585.     var count = aDragSession.numDropItems;
  586.     for (var i = 0; i < count; ++i) {
  587.       var xfer = Components.classes["@mozilla.org/widget/transferable;1"]
  588.                           .createInstance(Components.interfaces.nsITransferable);
  589.       xfer.addDataFlavor("text/x-moz-url");
  590.       aDragSession.getData(xfer, i);
  591.       
  592.       var data = { }, length = { };
  593.       xfer.getTransferData("text/x-moz-url", data, length);
  594.       var fileURL = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
  595.       var uri = Components.classes["@mozilla.org/network/standard-url;1"]
  596.                           .createInstance(Components.interfaces.nsIURI);
  597.       uri.spec = fileURL;
  598.       var url = uri.QueryInterface(Components.interfaces.nsIURL);
  599.       if (url.fileExtension == "xpi") {
  600.         xpinstallObj[url.fileName] = fileURL;
  601.         ++xpiCount;
  602.       }
  603.       else if (url.fileExtension == "jar") {
  604.         themes[url.fileName] = fileURL;
  605.         ++themeCount;
  606.       }
  607.     }
  608.     if (xpiCount > 0) 
  609.       InstallTrigger.install(xpinstallObj);
  610.     if (themeCount > 0) {
  611.       for (var fileName in themes)
  612.         InstallTrigger.installChrome(InstallTrigger.SKIN, themes[fileName], fileName);
  613.     }
  614.   },
  615.   _flavourSet: null,  
  616.   getSupportedFlavours: function ()
  617.   {
  618.     if (!this._flavourSet) {
  619.       this._flavourSet = new FlavourSet();
  620.       this._flavourSet.appendFlavour("text/x-moz-url");
  621.     }
  622.     return this._flavourSet;
  623.   }
  624. }
  625.  
  626. ///////////////////////////////////////////////////////////////////////////////
  627. // Command Updating and Command Handlers
  628.  
  629. var gExtensionsViewController = {
  630.   supportsCommand: function (aCommand)
  631.   {
  632.     var commandNode = document.getElementById(aCommand);
  633.     return commandNode && (commandNode.parentNode == document.getElementById("extensionsCommands"));
  634.   },
  635.   
  636.   isCommandEnabled: function (aCommand)
  637.   {
  638.     var selectedItem = gExtensionsView.selected;
  639.     switch (aCommand) {
  640.     case "cmd_close":
  641.       return true;
  642.     case "cmd_useTheme":
  643.       return selectedItem &&
  644.              !selectedItem.disabled &&
  645.              selectedItem.getAttribute("toBeUninstalled") != "true" &&
  646.              gCurrentTheme != selectedItem.getAttribute("internalName");
  647.     case "cmd_options":
  648.       return selectedItem &&
  649.              !selectedItem.disabled &&
  650.              selectedItem.getAttribute("toBeUninstalled") != "true" &&
  651.              selectedItem.getAttribute("optionsURL") != "";
  652.     case "cmd_about":
  653.       return selectedItem && (selectedItem.disabled ? selectedItem.getAttribute("aboutURL") == "" : true);
  654.     case "cmd_homepage":
  655.       return selectedItem && selectedItem.getAttribute("homepageURL") != "";
  656.     case "cmd_uninstall":
  657.       if (gWindowState != "extensions") {
  658.         // uninstall is only available if the selected item isn't the 
  659.         // default theme.
  660.         return (selectedItem && 
  661.                 selectedItem.getAttribute("internalName") != KEY_DEFAULT_THEME);
  662.       }
  663.       return selectedItem &&
  664.              selectedItem.getAttribute("toBeUninstalled") != "true" &&
  665.              selectedItem.getAttribute("locked") != "true";
  666.     case "cmd_update":
  667.       return !selectedItem ||
  668.              (selectedItem &&
  669.               selectedItem.getAttribute("toBeUninstalled") != "true" &&
  670.               selectedItem.getAttribute("toBeInstalled") != "true");
  671.     case "cmd_reallyEnable":
  672.     // controls whether to show Enable or Disable in extensions' context menu
  673.       return selectedItem && 
  674.              selectedItem.disabled && 
  675.              !gExtensionManager.inSafeMode;
  676.     case "cmd_enable":
  677.     //controls wheter the Enable/Disable menuitem is enabled
  678.       return selectedItem && 
  679.              selectedItem.disabled && 
  680.              !gExtensionManager.inSafeMode && 
  681.              selectedItem.getAttribute("toBeUninstalled") != "true" &&
  682.              selectedItem.getAttribute("compatible") != "false";
  683.     case "cmd_disable":
  684.       return selectedItem &&
  685.              !selectedItem.disabled &&
  686.              selectedItem.getAttribute("toBeUninstalled") != "true" &&
  687.              selectedItem.getAttribute("locked") != "true";
  688.     case "cmd_movetop":
  689.       return selectedItem && (gExtensionsView.children[0] != selectedItem);
  690.     case "cmd_moveup":
  691.       return selectedItem && (gExtensionsView.children[0] != selectedItem);
  692.     case "cmd_movedn":
  693.       var children = gExtensionsView.children;
  694.       return selectedItem && (children[children.length-1] != selectedItem);
  695.     case "cmd_install":
  696.       return true;   
  697.     }
  698.     return false;
  699.   },
  700.  
  701.   doCommand: function (aCommand)
  702.   {
  703.     if (this.isCommandEnabled(aCommand))
  704.       this.commands[aCommand](gExtensionsView.selected);
  705.   },  
  706.   
  707.   onCommandUpdate: function ()
  708.   {
  709.     var extensionsCommands = document.getElementById("extensionsCommands");
  710.     for (var i = 0; i < extensionsCommands.childNodes.length; ++i) {
  711.       var command = extensionsCommands.childNodes[i];
  712.       if (this.isCommandEnabled(command.id))
  713.         command.removeAttribute("disabled");
  714.       else
  715.         command.setAttribute("disabled", "true");
  716.     }
  717.   },
  718.   
  719.   commands: { 
  720.     cmd_close: function (aSelectedItem)
  721.     {
  722.       closeWindow(true);
  723.     },  
  724.   
  725.     cmd_useTheme: function (aSelectedItem)
  726.     {
  727.       var pref = Components.classes["@mozilla.org/preferences-service;1"]
  728.                            .getService(Components.interfaces.nsIPrefBranch);
  729.       gCurrentTheme = aSelectedItem.getAttribute("internalName");
  730.       // Set this pref so the user can reset the theme in safe mode
  731.       pref.setCharPref(PREF_EM_LAST_SELECTED_SKIN, gCurrentTheme);
  732.  
  733.       if (pref.getBoolPref(PREF_EXTENSIONS_DSS_ENABLED)) {
  734.         pref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, gCurrentTheme);
  735.       }
  736.       else {
  737.         // Theme change will happen on next startup, this flag tells
  738.         // the Theme Manager that it needs to show "This theme will
  739.         // be selected after a restart" text in the selected theme
  740.         // item.
  741.         pref.setBoolPref(PREF_EXTENSIONS_DSS_SWITCHPENDING, true);
  742.         // Update the view
  743.         setRestartMessage(aSelectedItem);
  744.       }
  745.       
  746.       // disable the useThemeButton
  747.       gExtensionsViewController.onCommandUpdate();
  748.     },
  749.       
  750.     cmd_options: function (aSelectedItem)
  751.     {
  752.       if (!aSelectedItem) return;
  753.       var optionsURL = aSelectedItem.getAttribute("optionsURL");
  754.       if (optionsURL != "")
  755.         openDialog(optionsURL, "", "chrome,modal");
  756.     },
  757.     
  758.     cmd_homepage: function (aSelectedItem)
  759.     {
  760.       if (!aSelectedItem) return;
  761.       var homepageURL = aSelectedItem.getAttribute("homepageURL");
  762.       if (homepageURL != "")
  763.         openURL(homepageURL);
  764.     },
  765.     
  766.     cmd_about: function (aSelectedItem)
  767.     {
  768.       if (!aSelectedItem) return;
  769.       var aboutURL = aSelectedItem.getAttribute("aboutURL");
  770.       if (aboutURL != "")
  771.         openDialog(aboutURL, "", "chrome,modal");
  772.       else
  773.         openDialog("chrome://mozapps/content/extensions/about.xul", "", "chrome,modal", aSelectedItem.id, gExtensionsView.database);
  774.     },  
  775.     
  776.     cmd_movetop: function (aSelectedItem)
  777.     {
  778.       var movingID = aSelectedItem.id;
  779.       gExtensionManager.moveTop(stripPrefix(movingID, gItemType));
  780.       gExtensionsView.selected = document.getElementById(movingID);
  781.     },
  782.     
  783.     cmd_moveup: function (aSelectedItem)
  784.     {
  785.       var movingID = aSelectedItem.id;
  786.       gExtensionManager.moveUp(stripPrefix(movingID, gItemType));
  787.       gExtensionsView.selected = document.getElementById(movingID);
  788.     },
  789.     
  790.     cmd_movedn: function (aSelectedItem)
  791.     {
  792.       var movingID = aSelectedItem.id;
  793.       gExtensionManager.moveDown(stripPrefix(movingID, gItemType));
  794.       gExtensionsView.selected = document.getElementById(movingID);
  795.     },
  796.     
  797.     cmd_update: function (aSelectedItem)
  798.     { 
  799.       var id = aSelectedItem ? stripPrefix(aSelectedItem.id, gItemType) : null;
  800.       var itemType = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME;
  801.       var items = gExtensionManager.getItemList(id, itemType, { });
  802.       var updates = Components.classes["@mozilla.org/updates/update-service;1"]
  803.                               .getService(Components.interfaces.nsIUpdateService);
  804.       updates.checkForUpdates(items, items.length, itemType, 
  805.                               Components.interfaces.nsIUpdateService.SOURCE_EVENT_USER,
  806.                               window);
  807.     },
  808.  
  809.     cmd_uninstall: function (aSelectedItem)
  810.     {
  811.       // Confirm the uninstall
  812.       var extensionsStrings = document.getElementById("extensionsStrings");
  813.       var brandStrings = document.getElementById("brandStrings");
  814.  
  815.       var name = aSelectedItem.getAttribute("name");
  816.       var title = extensionsStrings.getFormattedString("queryUninstallTitle", [name]);
  817.       if (gWindowState == "extensions")
  818.         message = extensionsStrings.getFormattedString("queryUninstallExtensionMessage", [name, name]);
  819.       else if (gWindowState == "themes")
  820.         message = extensionsStrings.getFormattedString("queryUninstallThemeMessage", [name]);
  821.  
  822.       // XXXben - improve the wording on the buttons here!
  823.       var promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  824.                                 .getService(Components.interfaces.nsIPromptService);
  825.       if (!promptSvc.confirm(window, title, message))
  826.         return;
  827.  
  828.       var selectedID = aSelectedItem.id;
  829.       var selectedElement = document.getElementById(selectedID);
  830.       var nextElement = selectedElement.nextSibling;
  831.       if (!nextElement)
  832.         nextElement = selectedElement.previousSibling;
  833.       nextElement = nextElement.id;
  834.       
  835.       if (gWindowState == "extensions")
  836.         gExtensionManager.uninstallExtension(stripPrefix(selectedID, gItemType));
  837.       else if (gWindowState == "themes") {
  838.         // If the theme being uninstalled is the current theme, we need to reselect
  839.         // the default. 
  840.         var pref = Components.classes["@mozilla.org/preferences-service;1"]
  841.                              .getService(Components.interfaces.nsIPrefBranch);
  842.         var currentTheme = pref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
  843.         if (aSelectedItem.getAttribute("internalName") == currentTheme)
  844.           this.cmd_useTheme(document.getElementById("urn:mozilla:theme:{972ce4c6-7e08-4474-a285-3208198ce6fd}"));
  845.         gExtensionManager.uninstallTheme(stripPrefix(selectedID, gItemType));
  846.  
  847.       }
  848.       
  849.       gExtensionsView.selected = document.getElementById(nextElement);
  850.     },
  851.     
  852.     cmd_disable: function (aSelectedItem)
  853.     {
  854.       gExtensionManager.disableExtension(stripPrefix(aSelectedItem.id, gItemType));
  855.     },
  856.     
  857.     cmd_enable: function (aSelectedItem)
  858.     {
  859.       if (gWindowState == "extensions")
  860.         gExtensionManager.enableExtension(stripPrefix(aSelectedItem.id, gItemType));
  861.       else
  862.         gExtensionManager.enableTheme(stripPrefix(aSelectedItem.id, gItemType));
  863.     },
  864.     cmd_install: function(aSelectedItem)
  865.     {
  866.       if (gWindowState == "extensions") 
  867.         installExtension();
  868.       else
  869.         installSkin();
  870.     },
  871.   }
  872. };
  873.  
  874. ///////////////////////////////////////////////////////////////
  875. // functions to support installing of themes in thunderbird
  876. ///////////////////////////////////////////////////////////////
  877. const nsIFilePicker = Components.interfaces.nsIFilePicker;
  878. const nsIIOService = Components.interfaces.nsIIOService;
  879. const nsIFileProtocolHandler = Components.interfaces.nsIFileProtocolHandler;
  880. const nsIURL = Components.interfaces.nsIURL;
  881.  
  882. function installSkin()
  883. {
  884.   // 1) Prompt the user for the location of the theme to install. 
  885.   var extensionsStrings = document.getElementById("extensionsStrings");
  886.  
  887.   var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  888.   fp.init(window, extensionsStrings.getString("installThemePickerTitle"), nsIFilePicker.modeOpen);
  889.  
  890.  
  891.   fp.appendFilter(extensionsStrings.getString("themesFilter"), "*.jar");
  892.   fp.appendFilters(nsIFilePicker.filterAll);
  893.  
  894.   var ret = fp.show();
  895.   if (ret == nsIFilePicker.returnOK) 
  896.   {
  897.     var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(nsIIOService);
  898.     var fileProtocolHandler =
  899.     ioService.getProtocolHandler("file").QueryInterface(nsIFileProtocolHandler);
  900.     var url = fileProtocolHandler.newFileURI(fp.file).QueryInterface(nsIURL);
  901.     InstallTrigger.installChrome(InstallTrigger.SKIN, url.spec, decodeURIComponent(url.fileBaseName));
  902.   }
  903. }
  904.  
  905. function installExtension()
  906. {
  907.   var extensionsStrings = document.getElementById("extensionsStrings");
  908.  
  909.   var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  910.   fp.init(window, extensionsStrings.getString("installExtensionPickerTitle"), nsIFilePicker.modeOpen);
  911.   
  912.   fp.appendFilter(extensionsStrings.getString("extensionFilter"), "*.xpi");
  913.   
  914.   fp.appendFilters(nsIFilePicker.filterAll);
  915.  
  916.   var ret = fp.show();
  917.   if (ret == nsIFilePicker.returnOK) 
  918.   {
  919.     var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(nsIIOService);
  920.     var fileProtocolHandler =
  921.     ioService.getProtocolHandler("file").QueryInterface(nsIFileProtocolHandler);
  922.     var url = fileProtocolHandler.newFileURI(fp.file).QueryInterface(nsIURL);
  923.     var xpi = {};
  924.     xpi[decodeURIComponent(url.fileBaseName)] = url.spec;
  925.     InstallTrigger.install(xpi);
  926.   }
  927. }
  928.  
  929.